# sweetest/keywords/windows_tools.py
# 轻量 JSONL 客户端：把 Python 调用转成 {cmd,args} 发给 Node worker

import json
import subprocess
import threading
import queue
import time
import os

DEFAULT_WORKER = os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")), "playwright_worker.js")

class WindowsDriverProxy:
    """
    与 playwright_worker.js 通过 JSONL 协议交互：
      - launch_app(path/executablePath, args, waitMs)
      - wait_for(selector, timeout)
      - click(selector, timeout)
      - chain_relaunch(exePath, processName, waitMs)
      - exit()
    """

    def __init__(self, worker_path: str = None, **_):
        self.worker_js = worker_path or DEFAULT_WORKER
        self.proc = None
        self._lock = threading.Lock()
        self._out_q = queue.Queue()
        self._ensure_worker()

    # ---------- 进程与收发 ----------
    def _ensure_worker(self):
        if self.proc and self.proc.poll() is None:
            return
        self.proc = subprocess.Popen(
            ["node", self.worker_js],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            bufsize=1,
            universal_newlines=True,
        )
        threading.Thread(target=self._reader, daemon=True).start()
        time.sleep(0.1)  # 预热

    def _reader(self):
        for line in self.proc.stdout:
            line = (line or "").strip()
            if not line:
                continue
            self._out_q.put(line)

    def _rpc(self, cmd: str, args: dict, timeout: int = 30):
        self._ensure_worker()
        payload = json.dumps({"cmd": cmd, "args": args}, ensure_ascii=False)
        with self._lock:
            # 发送
            try:
                self.proc.stdin.write(payload + "\n")
                self.proc.stdin.flush()
            except Exception as e:
                raise RuntimeError(f"向 Node 发送失败：{e}")

            # 接收
            deadline = time.time() + timeout
            while time.time() < deadline:
                try:
                    line = self._out_q.get(timeout=0.2)
                except queue.Empty:
                    continue
                try:
                    resp = json.loads(line)
                except Exception as e:
                    # 忽略非 JSON 噪声（如 Playwright 启动日志）
                    continue
                if not resp.get("ok"):
                    raise RuntimeError(f"Node 执行失败：{cmd} -> {resp.get('error')}\n{resp.get('stack') or ''}")
                return resp.get("result")
            raise TimeoutError(f"等待 Node 响应超时：{cmd}")

    # ---------- 5 个稳定指令 ----------
    def launch_app(self, name: str = "main", path: str = None, executablePath: str = None,
                   args=None, wait_ms: int = 12000):
        exe = path or executablePath
        return self._rpc("launch_app", {
            "name": name, "path": exe, "executablePath": exe,
            "args": args or [], "waitMs": int(wait_ms or 0)
        }, timeout=max(30, (wait_ms // 1000) + 10))

    def wait_for(self, selector: str, timeout_ms: int = 10000):
        return self._rpc("wait_for", {
            "selector": selector, "timeout": int(timeout_ms or 0)
        }, timeout=max(20, (timeout_ms // 1000) + 5))

    def click(self, selector: str, timeout_ms: int = 10000):
        print('执行了click')
        return self._rpc("click", {
            "selector": selector, "timeout": int(timeout_ms or 0)
        }, timeout=max(20, (timeout_ms // 1000) + 5))

    def type(self, selector: str, text: str, delay_ms: int = 0, timeout_ms: int = 10000):
        """
        向当前页面上由 selector 定位到的元素输入文本。
        - selector: 选择器，和 click 的写法一致（如 'xpath=//input[@name="path"]'）
        - text: 要输入的文本
        - delay_ms: 每个字符的输入间隔（可选）
        - timeout_ms: 查找元素和完成输入的超时（毫秒）
        """
        return self._rpc(
            "type",
            {
                "selector": selector,
                "text": str(text) if text is not None else "",
                "delay": int(delay_ms or 0),
                "timeout": int(timeout_ms or 0),
            },
            timeout=max(20, (timeout_ms // 1000) + 5),
        )

    def chain_relaunch(self, exe_path: str, process_name: str = "MaxOpticsStudio.exe", wait_ms: int = 8000):
        return self._rpc("chain_relaunch", {
            "exePath": exe_path, "processName": process_name, "waitMs": int(wait_ms or 0)
        }, timeout=max(40, (wait_ms // 1000) + 15))

    def stub_save_dialog(self, file_path: str):
        """在主进程 stub 掉 dialog.showSaveDialog/Sync，返回固定的 file_path"""
        return self._rpc("stub_save_dialog", {"filePath": str(file_path)})

    def restore_dialog(self):
        """恢复 dialog.showSaveDialog/Sync 原实现"""
        return self._rpc("restore_dialog", {})

    def stub_message_box(self, choice: str = "OK", once: bool = True):
        return self._rpc("stub_message_box", {"choice": choice, "once": bool(once)})



    def send_keys(self, text):
        """
        向当前激活的系统窗口发送按键文本（用于原生对话框）
        例：C:\\work\\cases\\demo.mop，或 {ENTER}、{TAB}
        """
        return self._rpc('send_keys', {'text': str(text)})

    def press(self, keys: str, selector: str = "", times: int = 1, timeout_ms: int = 10000):
        """
        跨平台键盘按键（走 Playwright keyboard.press）：
        - keys: 'Enter' / 'Control+S' / 'Alt+F4' / 'Shift+Tab' ...
        - selector: 可选；若提供则先聚焦该元素再 press
        - times: 可选；重复按的次数
        - timeout_ms: 可选；等待 selector 可见/可点的超时
        """
        payload = {"keys": str(keys)}
        if selector:
            payload["selector"] = str(selector)
        if times and int(times) > 1:
            payload["times"] = int(times)
        if timeout_ms:
            payload["timeout"] = int(timeout_ms)

        return self._rpc("press", payload, timeout=max(20, (timeout_ms // 1000) + 5))

    def sleep(self, ms: int):
        """毫秒级等待（用来等系统弹窗稳定）"""
        return self._rpc("sleep", {"ms": int(ms)})

    def kill_process(self, proc_name: str):
        if not proc_name:
            raise ValueError("kill_process 需要传入进程名，例如 'MaxOpticsStudio.exe' 或 'MaxOpticsStudio'")
        return self._rpc("kill_process", {"name": str(proc_name)}, timeout=10)  # ← 改这里

    def set_clipboard(self, text: str):
        """用 PowerShell 设置系统剪贴板（避免中文/特殊字符逐字输入的问题）"""
        # 注意转义双引号
        esc = str(text).replace('"', r'\"')
        subprocess.run(
            ["powershell", "-NoProfile", "-Command", f'Set-Clipboard -Value "{esc}"'],
            check=True, creationflags=0x08000000  # CREATE_NO_WINDOW，避免弹出窗口
        )

    def input(self, selector: str, text: str, timeout: int = 10000, delay: int = 0):
        """
        对选中的输入框执行“清空并填充”。
        - selector: 元素选择器（xpath= / css= …），或由测试框架解析出的定位串
        - text: 要输入的文本
        """
        args = {
            "selector": str(selector),
            "text": str(text),
            "replace": True,  # 关键点：清空后填充
            "timeout": int(timeout),
            "delay": int(delay),
        }
        return self._rpc("type", args)

    def exit(self):
        try:
            return self._rpc("exit", {}, timeout=10)
        finally:
            try:
                self.proc.terminate()
            except Exception:
                pass
